using System;

[assembly: CLSCompliant(true)]

namespace DataClasses
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Linq;
    using System.Runtime.Serialization;
    using MySql.Data.MySqlClient;

    /// <summary>
    /// Base class for working with database
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [DataContract]
    public abstract class ActiveRecord<T> where T : ActiveRecord<T>, new()
    {
        #region DB Members
        /// <summary>
        /// This variable is used like flag to check the initialization
        /// of non-static members of activerecord
        /// </summary>
        public static bool IsInitNStatic;

        [DataMember]
        protected object[] objects;

        [DataMember]
        private readonly static MySqlConnection connection = new MySqlConnection(@"SERVER=localhost;DATABASE=cypdb;UID=someuser;");
        private static string selidstr;
        private static string select;
        private static string truncate;
        private static string table;
        private static bool _isInitStatic;
        private static string update;
        private static string delete;
        private static string insert;
        

        private Guid _id;

        static ActiveRecord()
        {
            if (_isInitStatic)
            {
                return;
            }

            StaticInit();
        }

        protected ActiveRecord()
        {
            if (IsInitNStatic)
            {
                return;
            }

            this.Initialize();
        }

        /// <summary>
        /// This property is virtual, 'cause here will be saved all of the fields
        /// of each model in DAL
        /// </summary>
        public virtual object[] Objparams { get; set; }

        /// <summary>
        /// This is simple id for identifying each model entity
        /// </summary>
        [DataMember]
        public Guid Id { get { return _id; } set { _id = value; } }

        protected MySqlConnection Connection { get { return connection; } }

        private static string Dtable
        {
            get
            {
                return table ?? (table = typeof(T).Name.ToLower());
            }
        }

        #endregion

        #region Save , Delete , Get , GetById

        /// <summary>
        /// Static method Get to get all records of current model from DB
        /// </summary>
        /// <returns>List of current model</returns>
        public static List<T> Get()
        {
            if (!IsInitNStatic)
            {
                new T();
            }

            var selectCommand = new MySqlCommand(select, connection);

            using (connection)
            {
                connection.Open();
                var reader = selectCommand.ExecuteReader();
                var tlist = new List<T>();
                while (reader.Read())
                {
                    var temp = new object[reader.FieldCount];
                    reader.GetValues(temp);
                    var cln = new T { Objparams = temp };
                    tlist.Add(cln);
                }

                return tlist;
            }
        } 

        /// <summary>
        /// Method which return list of records with such id
        /// </summary>
        /// <param name="guid"></param>
        /// <param name="key"></param>
        /// <returns>
        /// List of current model
        /// </returns>
        public static List<T> GetById(Guid guid, string key = null)
        {
            if (!IsInitNStatic)
            {
                new T();
            }

            if (key == null)
            {
                key = "id";
            }

            var t = new List<T>();
            var selectid = new MySqlCommand(string.Format(selidstr + "`{0}`=@value", key), connection);
            selectid.Parameters.Add("@value", MySqlDbType.Binary).Value = guid.ToByteArray();
            using (connection)
            {
                connection.Open();
                var reader = selectid.ExecuteReader();
                while (reader.Read())
                {
                    var temp = new object[reader.FieldCount];
                    reader.GetValues(temp);
                    var cln = new T { Objparams = temp };
                    t.Add(cln);
                }
            }

            return t;
        }

        /// <summary>
        /// Simple void for delete all records in current table
        /// </summary>
        public static void DeleteAll()
        {
            var truncateCommand = new MySqlCommand(truncate, connection);
            using (connection)
            {
                connection.Open();
                truncateCommand.ExecuteNonQuery();
            }
        }

        /// <summary>
        /// Saves your current entity to the DB
        /// </summary>
        public void Save()
        {
            if (!_isInitStatic)
            {
                StaticInit();
            }

            var command = new MySqlCommand(insert, connection);
            var count = insert.Count(x => x == '@');
            for (var i = 0; i < count; i++)
            {
                command.Parameters.AddWithValue(string.Format("@p{0}", i + 1), Objparams[i]);
            }

            connection.Open();
            using (connection)
            {
                command.ExecuteNonQuery();
                connection.Close();
            }
        }

        /// <summary>
        /// Deletes current entity
        /// </summary>
        /// <param name="guid"></param>
        public void Delete(Guid? guid = null)
        {
            var deleteCommand = new MySqlCommand(delete, connection);
            if (guid == null)
            {
                guid = Id;
            }

            using (connection)
            {
                connection.Open();
                deleteCommand.Parameters.Add("@id", MySqlDbType.Binary).Value = guid.Value.ToByteArray();
                deleteCommand.ExecuteNonQuery();
            }
        }

        #endregion

        #region Initialazation

        private static void StaticInit()
        {
            select = string.Format("SELECT * FROM {0}", Dtable);
            selidstr = select + " WHERE ";
            truncate = string.Format("TRUNCATE TABLE {0}", Dtable);
            _isInitStatic = true;
        }

        private void Initialize()
        {
            var adapter = new MySqlDataAdapter();
            var cmdbuilder = new MySqlCommandBuilder();
            var dataTab = new DataTable();
            adapter.SelectCommand = new MySqlCommand(select, connection);
            cmdbuilder.DataAdapter = adapter;
            GetCommands(adapter, cmdbuilder);
            adapter.Fill(dataTab);

            IsInitNStatic = true;
        }

        private void GetCommands(MySqlDataAdapter adapter, MySqlCommandBuilder cmdbuilder)
        {
            adapter.InsertCommand = cmdbuilder.GetInsertCommand();
            adapter.UpdateCommand = cmdbuilder.GetUpdateCommand();
            insert = adapter.InsertCommand.CommandText;
            update = adapter.UpdateCommand.CommandText;
            delete = string.Format("DELETE FROM {0} WHERE `id`=@id", Dtable);
            update = RecieveQuery(adapter.UpdateCommand.CommandText);
        }

        private string RecieveQuery(string query)
        {
            query = query.Remove(query.IndexOf('('), 1);
            query = query.Remove(query.IndexOf("AND"));
            return query;
        }

        #endregion
    }
}
